/*
 * This file is part of aion-engine <aion-engine.com>
 *
 * aion-engine is private software: you can redistribute it and or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Private Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aion-engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser Public License
 * along with aion-engine.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionengine.gameserver.model.templates.spawns;

import com.aionemu.commons.taskmanager.AbstractLockManager;
import com.aionemu.commons.utils.Rnd;
import com.aionengine.gameserver.model.Race;
import com.aionengine.gameserver.model.siege.SiegeModType;
import com.aionengine.gameserver.model.siege.SiegeRace;
import com.aionengine.gameserver.model.templates.spawns.basespawns.BaseSpawnTemplate;
import com.aionengine.gameserver.model.templates.spawns.riftspawns.RiftSpawnTemplate;
import com.aionengine.gameserver.model.templates.spawns.siegespawns.SiegeSpawnTemplate;
import com.aionengine.gameserver.model.templates.spawns.vortexspawns.VortexSpawnTemplate;
import com.aionengine.gameserver.model.vortex.VortexStateType;
import com.aionengine.gameserver.spawnengine.SpawnHandlerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author xTz
 * @modified Rolandas
 */
public class SpawnGroup2 extends AbstractLockManager {

    private static final Logger log = LoggerFactory.getLogger(SpawnGroup2.class);

    private int worldId;
    private int npcId;
    private int pool;
    private byte difficultId;
    private TemporarySpawn temporarySpawn;
    private int respawnTime;
    private SpawnHandlerType handlerType;
    private List<SpawnTemplate> spots = new ArrayList<SpawnTemplate>();
    private HashMap<Integer, HashMap<SpawnTemplate, Boolean>> poolUsedTemplates;

    public SpawnGroup2(int worldId, Spawn spawn) {
        this.worldId = worldId;
        initializing(spawn);
        for (SpawnSpotTemplate template : spawn.getSpawnSpotTemplates()) {
            SpawnTemplate spawnTemplate = new SpawnTemplate(this, template);
            if (spawn.isEventSpawn())
                spawnTemplate.setEventTemplate(spawn.getEventTemplate());
            spots.add(spawnTemplate);
        }
    }

    public SpawnGroup2(int worldId, Spawn spawn, int id, Race race) {
        this.worldId = worldId;
        initializing(spawn);
        for (SpawnSpotTemplate template : spawn.getSpawnSpotTemplates()) {
            BaseSpawnTemplate spawnTemplate = new BaseSpawnTemplate(this, template);
            spawnTemplate.setId(id);
            spawnTemplate.setBaseRace(race);
            spots.add(spawnTemplate);
        }
    }

    public SpawnGroup2(int worldId, Spawn spawn, int id) {
        this.worldId = worldId;
        initializing(spawn);
        for (SpawnSpotTemplate template : spawn.getSpawnSpotTemplates()) {
            RiftSpawnTemplate spawnTemplate = new RiftSpawnTemplate(this, template);
            spawnTemplate.setId(id);
            spots.add(spawnTemplate);
        }
    }

    public SpawnGroup2(int worldId, Spawn spawn, int id, VortexStateType type) {
        this.worldId = worldId;
        initializing(spawn);
        for (SpawnSpotTemplate template : spawn.getSpawnSpotTemplates()) {
            VortexSpawnTemplate spawnTemplate = new VortexSpawnTemplate(this, template);
            spawnTemplate.setId(id);
            spawnTemplate.setStateType(type);
            spots.add(spawnTemplate);
        }
    }

    public SpawnGroup2(int worldId, Spawn spawn, int siegeId, SiegeRace race, SiegeModType mod) {
        this.worldId = worldId;
        initializing(spawn);
        for (SpawnSpotTemplate template : spawn.getSpawnSpotTemplates()) {
            SiegeSpawnTemplate spawnTemplate = new SiegeSpawnTemplate(this, template);
            spawnTemplate.setSiegeId(siegeId);
            spawnTemplate.setSiegeRace(race);
            spawnTemplate.setSiegeModType(mod);
            spots.add(spawnTemplate);
        }
    }

    private void initializing(Spawn spawn) {
        temporarySpawn = spawn.getTemporarySpawn();
        respawnTime = spawn.getRespawnTime();
        pool = spawn.getPool();
        npcId = spawn.getNpcId();
        handlerType = spawn.getSpawnHandlerType();
        difficultId = spawn.getDifficultId();
        if (hasPool())
            poolUsedTemplates = new HashMap<Integer, HashMap<SpawnTemplate, Boolean>>();
    }

    public SpawnGroup2(int worldId, int npcId) {
        this.worldId = worldId;
        this.npcId = npcId;
    }

    public List<SpawnTemplate> getSpawnTemplates() {
        return spots;
    }

    public void addSpawnTemplate(SpawnTemplate spawnTemplate) {
        super.writeLock();
        try {
            spots.add(spawnTemplate);
        } finally {
            super.writeUnlock();
        }
    }

    public int getWorldId() {
        return worldId;
    }

    public int getNpcId() {
        return npcId;
    }

    public TemporarySpawn geTemporarySpawn() {
        return temporarySpawn;
    }

    public int getPool() {
        return pool;
    }

    public boolean hasPool() {
        return pool > 0;
    }

    public int getRespawnTime() {
        return respawnTime;
    }

    public void setRespawnTime(int respawnTime) {
        this.respawnTime = respawnTime;
    }

    public boolean isTemporarySpawn() {
        return temporarySpawn != null;
    }

    public SpawnHandlerType getHandlerType() {
        return handlerType;
    }

    public SpawnTemplate getRndTemplate(int instanceId) {
        final List<SpawnTemplate> allTemplates = spots;
        List<SpawnTemplate> templates = new ArrayList<SpawnTemplate>();
        super.readLock();
        try {
            for (SpawnTemplate template : allTemplates) {
                if (!isTemplateUsed(instanceId, template)) {
                    templates.add(template);
                }
            }
            if (templates.size() == 0) {
                log.warn("Pool size more then spots, npcId: " + npcId + ", worldId: " + worldId);
                return null;
            }
        } finally {
            super.readUnlock();
        }
        SpawnTemplate spawnTemplate = templates.get(Rnd.get(0, templates.size() - 1));
        setTemplateUse(instanceId, spawnTemplate, true);
        return spawnTemplate;
    }

    public void setTemplateUse(int instanceId, SpawnTemplate template, boolean isUsed) {
        super.writeLock();
        try {
            HashMap<SpawnTemplate, Boolean> states = poolUsedTemplates.get(instanceId);
            if (states == null) {
                states = new HashMap<SpawnTemplate, Boolean>();
                poolUsedTemplates.put(instanceId, states);
            }
            states.put(template, isUsed);
        } finally {
            super.writeUnlock();
        }
    }

    public boolean isTemplateUsed(int instanceId, SpawnTemplate template) {
        super.readLock();
        try {
            HashMap<SpawnTemplate, Boolean> states = poolUsedTemplates.get(instanceId);
            if (states == null)
                return false;
            Boolean state = states.get(template);
            if (state == null)
                return false;
            return state;
        } finally {
            super.readUnlock();
        }
    }

    /**
     * Call it before each randomization to unset all template use.
     *
     * @param instanceId
     */
    public void resetTemplates(int instanceId) {
        HashMap<SpawnTemplate, Boolean> states = poolUsedTemplates.get(instanceId);
        if (states == null)
            return;
        super.writeLock();
        try {
            for (SpawnTemplate template : states.keySet()) {
                states.put(template, false);
            }
        } finally {
            super.writeUnlock();
        }
    }

    public byte getDifficultId() {
        return difficultId;
    }

}
